package cn.botter.subject.infra.basic.service.impl;

import cn.botter.subject.common.entity.PageResult;
import cn.botter.subject.common.enums.SubjectInfoTypeEnum;
import cn.botter.subject.infra.basic.entity.EsSubjectFields;
import cn.botter.subject.infra.basic.entity.SubjectInfoES;
import cn.botter.subject.infra.basic.es.EsIndexInfo;
import cn.botter.subject.infra.basic.es.EsRestClient;
import cn.botter.subject.infra.basic.es.EsSearchRequest;
import cn.botter.subject.infra.basic.es.EsSourceData;
import cn.botter.subject.infra.basic.service.SubjectESService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @BelongsProject: bo-club
 * @BelongsPackage: cn.botter.subject.infra.basic.service.impl
 * @Author: 爱写Bug的Botter
 * @CreateTime: 2025-04-03:14:30
 * @Description: 描述
 * @Version: 1.0
 */
@Service
@Slf4j
public class SubjectESServiceImpl implements SubjectESService {
    public final static EsIndexInfo esIndexInfo = new EsIndexInfo("subject_index" ,
            "a017fe54f1b3");
    @Override
    public boolean insert(SubjectInfoES subjectInfoES) {
        EsSourceData esSourceData = new EsSourceData();
        esSourceData.setDocId(subjectInfoES.getDocId().toString());
        esSourceData.setData(converter2EsSourceDate(subjectInfoES));
        return EsRestClient.insertDoc(esIndexInfo , esSourceData);
    }

    private Map<String, Object> converter2EsSourceDate(SubjectInfoES subjectInfoES) {
        Map<String, Object> map = new HashMap<>();
        map.put(EsSubjectFields.DOC_ID, subjectInfoES.getDocId());
        map.put(EsSubjectFields.SUBJECT_ANSWER, subjectInfoES.getSubjectAnswer());
        map.put(EsSubjectFields.SUBJECT_NAME, subjectInfoES.getSubjectName());
        map.put(EsSubjectFields.SUBJECT_TYPE, subjectInfoES.getSubjectType());
        map.put(EsSubjectFields.SUBJECT_ID, subjectInfoES.getSubjectId());
        map.put(EsSubjectFields.CREATE_USER, subjectInfoES.getCreateUser());
        map.put(EsSubjectFields.CREATE_TIME, subjectInfoES.getCreateTime());
        return map;
    }

    @Override
    public PageResult<SubjectInfoES> querySubjectList(SubjectInfoES req) {
//        PageResult<SubjectInfoES> pageResult = new PageResult<>();
//        List<SubjectInfoES> subjectInfoESList = new ArrayList<>();
//        EsSearchRequest esSearchRequest = createSearchListQuery(req);
//        SearchResponse searchResponse = EsRestClient.searchWithTermQuery(esIndexInfo, esSearchRequest);
//        //将searchResponse转换为我们要的PageResult
//        SearchHits searchHits = searchResponse.getHits(); //返回我们命中的东东
//        if (searchHits == null || searchHits.getHits().length == 0) { //没有命中东西
//            pageResult.setResult(subjectInfoESList);
//            pageResult.setPageSize(req.getPageSize());
//            pageResult.setTotal(0);
//            pageResult.setPageNo(req.getPageNo());
//            return pageResult;
//        }
//        for (SearchHit hit : searchHits.getHits()) {
//            SubjectInfoES subjectInfoES = new SubjectInfoES();
//            subjectInfoES = converterResult(hit);
//            if (subjectInfoES != null) {
//                subjectInfoESList.add(subjectInfoES);
//            }
//        }
//        pageResult.setResult(subjectInfoESList);
//        pageResult.setPageNo(req.getPageNo());
//        pageResult.setPageSize(req.getPageSize());
//        pageResult.setTotal((int) searchHits.getTotalHits().value);
//        return pageResult;
        PageResult<SubjectInfoES> pageResult = new PageResult<>();
        EsSearchRequest esSearchRequest = createSearchListQuery(req);
        SearchResponse searchResponse = EsRestClient.searchWithTermQuery(esIndexInfo, esSearchRequest);

        List<SubjectInfoES> subjectInfoEsList = new LinkedList<>();
        SearchHits searchHits = searchResponse.getHits();
        if (searchHits == null || searchHits.getHits() == null) {
            pageResult.setPageNo(req.getPageNo());
            pageResult.setPageSize(req.getPageSize());
            pageResult.setResult(subjectInfoEsList);
            pageResult.setTotal(0);
            return pageResult;
        }
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            SubjectInfoES subjectInfoEs = converterResult(hit);
            if (Objects.nonNull(subjectInfoEs)) {
                subjectInfoEsList.add(subjectInfoEs);
            }
        }
        pageResult.setPageNo(req.getPageNo());
        pageResult.setPageSize(req.getPageSize());
        pageResult.setResult(subjectInfoEsList);
        pageResult.setTotal(Long.valueOf(searchHits.getTotalHits().value).intValue());
        return pageResult;
    }

    private SubjectInfoES converterResult(SearchHit hit) {
//        SubjectInfoES result = new SubjectInfoES();
//        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//        if (CollectionUtils.isNotEmpty(sourceAsMap)){
//            return null;
//        }
//        result.setSubjectId(MapUtils.getInteger(sourceAsMap,EsSubjectFields.SUBJECT_ID));
//        result.setSubjectName(MapUtils.getString(sourceAsMap,EsSubjectFields.SUBJECT_NAME));
//        result.setSubjectAnswer(MapUtils.getString(sourceAsMap,EsSubjectFields.SUBJECT_ANSWER));
//        result.setDocId(MapUtils.getLong(sourceAsMap,EsSubjectFields.DOC_ID));
//        result.setSubjectType(MapUtils.getInteger(sourceAsMap,EsSubjectFields.DOC_ID));
////        将返回的匹配度转换为百分比
//        result.setScore(new BigDecimal(String.valueOf(hit.getScore())).multiply(new BigDecimal("100.00"))
//                .setScale(2 , RoundingMode.HALF_UP));
//        //题目名的高亮
//        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//        HighlightField subjectNameField = highlightFields.get(EsSubjectFields.SUBJECT_NAME);
//        if (Objects.nonNull(subjectNameField)){
//            Text[] fragments = subjectNameField.getFragments();//相关的段落
//            StringBuilder subjectNameBuilder = new StringBuilder();
////            将段落拼接在一起
//            for (Text fragment : fragments) {
//                subjectNameBuilder.append(fragment);
//            }
//            result.setSubjectName(subjectNameBuilder.toString());
//        }
//        //处理题目答案的高亮
//        HighlightField subjectAnswer = highlightFields.get(EsSubjectFields.SUBJECT_ANSWER);
//        if (Objects.nonNull(subjectAnswer)){
//            Text[] fragments = subjectAnswer.getFragments();//相关的段落
//            StringBuilder subjectAnswerBuilder = new StringBuilder();
////            将段落拼接在一起
//            for (Text fragment : fragments) {
//                subjectAnswerBuilder.append(fragment);
//            }
//            result.setSubjectName(subjectAnswerBuilder.toString());
//        }
//        return result;
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
        if (CollectionUtils.isEmpty(sourceAsMap)) {
            return null;
        }
        SubjectInfoES result = new SubjectInfoES();
        result.setSubjectId(MapUtils.getInteger(sourceAsMap, EsSubjectFields.SUBJECT_ID));
        result.setSubjectName(MapUtils.getString(sourceAsMap, EsSubjectFields.SUBJECT_NAME));

        result.setSubjectAnswer(MapUtils.getString(sourceAsMap, EsSubjectFields.SUBJECT_ANSWER));

        result.setDocId(MapUtils.getLong(sourceAsMap, EsSubjectFields.DOC_ID));
        result.setSubjectType(MapUtils.getInteger(sourceAsMap, EsSubjectFields.SUBJECT_TYPE));
        result.setScore(new BigDecimal(String.valueOf(hit.getScore())).multiply(new BigDecimal("100.00")
                .setScale(2, RoundingMode.HALF_UP)));

        //处理name的高亮
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        HighlightField subjectNameField = highlightFields.get(EsSubjectFields.SUBJECT_NAME);
        if(Objects.nonNull(subjectNameField)){
            Text[] fragments = subjectNameField.getFragments();
            StringBuilder subjectNameBuilder = new StringBuilder();
            for (Text fragment : fragments) {
                subjectNameBuilder.append(fragment);
            }
            result.setSubjectName(subjectNameBuilder.toString());
        }

        //处理答案高亮
        HighlightField subjectAnswerField = highlightFields.get(EsSubjectFields.SUBJECT_ANSWER);
        if(Objects.nonNull(subjectAnswerField)){
            Text[] fragments = subjectAnswerField.getFragments();
            StringBuilder subjectAnswerBuilder = new StringBuilder();
            for (Text fragment : fragments) {
                subjectAnswerBuilder.append(fragment);
            }
            result.setSubjectAnswer(subjectAnswerBuilder.toString());
        }

        return result;
    }

    private EsSearchRequest createSearchListQuery(SubjectInfoES req) {
//        EsSearchRequest esSearchRequest = new EsSearchRequest();
//        BoolQueryBuilder bq = new BoolQueryBuilder();
//        //应该匹配的方法,就是关键字应该要匹配的是SubjectName（非强制)
//        MatchQueryBuilder subjectName = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_NAME, req.getKeyWord());
//        bq.should(subjectName);
//        subjectName.boost(2); //优先匹配标题，设置更高的权重
//        //应该匹配的方法,就是关键字应该要匹配的是subjectAnswer（非强制)
//        MatchQueryBuilder subjectAnswer = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_ANSWER, req.getKeyWord());
//        bq.should(subjectAnswer);
//        //必须要匹配的字段
//        MatchQueryBuilder subjectType = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_TYPE, SubjectInfoTypeEnum.BRIEF.getCode());
//        bq.must(subjectType);
//        bq.minimumShouldMatch(1); // 至少要满足的条件数量
//
//        //高亮设置
//        //        field("*"),表示什么都行，不需要匹配的字段
//        HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
//        //样式前缀
//        highlightBuilder.preTags("<span style='color:red'>");
//        //后缀
//        highlightBuilder.postTags("</span>");
//        esSearchRequest.setBq(bq);
//        esSearchRequest.setHighlight(highlightBuilder);
//        esSearchRequest.setFields(EsSubjectFields.FIELD_QUERY);
//        esSearchRequest.setFrom((req.getPageNo() - 1) * req.getPageSize());//分页起始行标
//        esSearchRequest.setSize(req.getPageSize());
//        esSearchRequest.setNeedScroll(false);
//        return esSearchRequest;
        EsSearchRequest esSearchRequest = new EsSearchRequest();
        BoolQueryBuilder bq = new BoolQueryBuilder();
        MatchQueryBuilder subjectNameQueryBuilder =
                QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_NAME, req.getKeyWord());

        bq.should(subjectNameQueryBuilder);
        subjectNameQueryBuilder.boost(2);

        MatchQueryBuilder subjectAnswerQueryBuilder =
                QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_ANSWER, req.getKeyWord());
        bq.should(subjectAnswerQueryBuilder);

        MatchQueryBuilder subjectTypeQueryBuilder =
                QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_TYPE, SubjectInfoTypeEnum.BRIEF.getCode());
        bq.must(subjectTypeQueryBuilder);
        bq.minimumShouldMatch(1);

        HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
        highlightBuilder.preTags("<span style = \"color:red\">");
        highlightBuilder.postTags("</span>");

        esSearchRequest.setBq(bq);
        esSearchRequest.setHighlight(highlightBuilder);
        esSearchRequest.setFields(EsSubjectFields.FIELD_QUERY);
        esSearchRequest.setFrom((req.getPageNo() - 1) * req.getPageSize());
        esSearchRequest.setSize(req.getPageSize());
        esSearchRequest.setNeedScroll(false);
        return esSearchRequest;
    }



}
